import os
import traceback
import math
import urllib3
from loguru import logger
from minio import Minio
from minio.error import S3Error
import sys
from pathlib import Path
ROOT_DIR = Path(__file__).parent.parent.absolute()
sys.path.append(str(ROOT_DIR))
from core.config import MINIO_COLD_ENDPOINT, MINIO_COLD_ACCESS_KEY, MINIO_COLD_SECRET_KEY
from core.config import leads
from core.utils import grb2_to_nc
def _convert_size(size_bytes):
    """
    将字节大小转换为人类可读的格式
    """
    if size_bytes is None or size_bytes == 0:
        return "0B"
    size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
    i = int(math.log(size_bytes, 1024))
    p = math.pow(1024, i)
    s = round(size_bytes / p, 2)
    return f"{s} {size_name[i]}"


def download_file_with_cert(client: Minio,bucket: str, file_name: str, file_path: str):
    """
    文件下载
    """
    # Make 'asiatrip' bucket if not exist.
    found = client.bucket_exists(bucket)
    if not found:
        logger.info("Bucket {} not exists".format(bucket))
        return
    try:
        client.fget_object(
            bucket_name=bucket, object_name=file_name, file_path=file_path
        )
        logger.info("file '{0}' is successfully download".format(file_name))
        return file_path
    except S3Error as err:
        logger.error("download_failed:", err)

def list_files_with_cert(client: Minio,bucket: str, prefix: str):
    """
    列出 MinIO 中的文件
    :param bucket: 存储桶名称
    :param prefix: 文件前缀
    :return: 文件信息列表，包含文件名、大小、最后修改时间、是否为目录等信息
    """
    logger.info(f"正在列出 {bucket} 桶中的 {prefix} 目录")
    resp = client.list_objects(bucket_name=bucket, prefix=prefix, recursive=True)  # 改为 recursive=True
    rtn_list = []
    for item in resp:
        # 跳过目录索引文件
        if item.object_name.endswith('/'):
            #logger.debug(f"跳过目录索引文件: {item.object_name}")
            continue
            
        file_info = {
            "name": item.object_name,  # 文件名
            "size": item.size if hasattr(item, "size") else 0,  # 文件大小（字节）
            "last_modified": item.last_modified,  # 最后修改时间
            "etag": item.etag,  # ETag
            "is_dir": item.is_dir,  # 是否为目录
            "content_type": item.content_type,  # 内容类型
            "owner_id": item.owner_id if hasattr(item, "owner_id") else None,  # 所有者ID
            "owner_name": item.owner_name
            if hasattr(item, "owner_name")
            else None,  # 所有者名称
            "size_human": _convert_size(
                item.size if hasattr(item, "size") else 0
            ),  # 人类可读的文件大小
        }
        rtn_list.append(file_info)
    
    logger.info(f"在 {prefix} 目录下找到 {len(rtn_list)} 个文件/文件夹")
    return rtn_list


def file_exists_with_cert(bucket: str, file_path: str):
    """
    文件是否存在
    """
    # Make 'asiatrip' bucket if not exist.
    resp = client.list_objects(bucket_name=bucket, prefix=file_path)
    for item in resp:
        logger.info(item.object_name)
        return True
    return False


def create_minio_client(
    endpoint: str, access_key: str, secret_key: str, secure: bool = True
):
    """
    创建 MinIO 客户端
    """
    try:
        # 创建自定义的 HTTP 客户端
        http_client = urllib3.PoolManager(
            timeout=urllib3.Timeout(connect=5, read=20),  # 设置超时
            maxsize=10,  # 连接池大小
            retries=urllib3.Retry(
                total=3,  # 总重试次数
                backoff_factor=0.5,  # 重试间隔
            ),
            cert_reqs="CERT_NONE",  # 禁用证书验证
        )

        # 创建 MinIO 客户端
        return Minio(
            endpoint.replace("https://", "").replace("http://", ""),
            access_key,
            secret_key,
            secure=secure,
            http_client=http_client,
        )
    except Exception as e:
        logger.error(f"创建 MinIO 客户端失败: {e}")
        raise


def download_folder_with_cert(
    client: Minio,
    bucket: str,
    minio_folder: str,
    local_folder: str,
    output_folder: str,
    leads: list[int],
    recursive: bool = True,
    convert_to_nc: bool = True,
) -> list[str]:
    """
    下载 MinIO 中指定文件夹的所有文件，并根据预见期重新组织文件结构

    参数:
        bucket: MinIO 存储桶名称
        minio_folder: MinIO 中的文件夹路径
        local_folder: 本地临时文件夹路径
        output_folder: 输出文件夹路径
        leads: 预见期列表
        recursive: 是否递归下载子文件夹，默认为 True
        convert_to_nc: 是否转换为NetCDF格式，默认为 True

    返回:
        List[str]: 下载成功的文件路径列表
    """
    # 确保本地文件夹存在
    os.makedirs(local_folder, exist_ok=True)

    # 获取文件列表
    files = list_files_with_cert(client,bucket, minio_folder)
    downloaded_files = []
    skipped_files = []

    for file_info in files:
        # 跳过文件夹
        if file_info["is_dir"]:
            if recursive:
                # 递归下载子文件夹
                sub_minio_folder = file_info["name"]
                sub_local_folder = os.path.join(
                    local_folder, os.path.basename(sub_minio_folder.rstrip("/"))
                )
                sub_output_folder = os.path.join(
                    output_folder, os.path.basename(sub_minio_folder.rstrip("/"))
                )
                sub_files = download_folder_with_cert(
                    client=client,
                    bucket=bucket,
                    minio_folder=sub_minio_folder,
                    local_folder=sub_local_folder,
                    output_folder=output_folder,
                    leads=leads,
                    recursive=recursive,
                    convert_to_nc=convert_to_nc
                )
                downloaded_files.extend(sub_files)
            continue

        try:
            # 检查文件是否匹配指定的预见期
            file_name = file_info["name"]

            # 提取预见期
            lead_match = None
            for lead in leads:
                if f"f{lead:03d}" in file_name:
                    lead_match = lead
                    break

            if lead_match is None:
                continue

            # 提取日期信息 (从路径中提取)
            # 假设路径格式为: GFS/CHN-FORECASAT/1h/YYYY/MM/DD/HH/gfs.tHHz.pgrb2.0p25.fXXX
            path_parts = file_name.split("/")
            if len(path_parts) >= 7:
                year = path_parts[-5]
                month = path_parts[-4]
                day = path_parts[-3]
                hour = path_parts[-2]
                # date_str = f"{year}{month}{day}{hour}"
                date_str = f"{year}{month}{day}{hour}"
            else:
                # 如果路径格式不匹配，使用当前日期
                import datetime

                date_str = datetime.datetime.now().strftime("%Y%m%d%H")

            # 构建新的文件名和路径
            new_file_name = f"gfs.0p25.{date_str}.f{lead_match:03d}.grib2"
            new_folder = os.path.join(output_folder, str(lead_match))
            os.makedirs(new_folder, exist_ok=True)
            new_file_path = os.path.join(new_folder, new_file_name)

            # 下载文件到临时位置
            temp_file_path = os.path.join(
                local_folder, "temp_" + os.path.basename(file_name)
            )
            os.makedirs(os.path.dirname(temp_file_path), exist_ok=True)

            result = download_file_with_cert(
                client=client, bucket=bucket, file_name=file_name, file_path=temp_file_path
            )

            if result:
                # 移动文件到新位置
                import shutil

                shutil.move(temp_file_path, new_file_path)
                logger.info(f"成功下载文件: {file_name} -> {new_file_path}")
                # 检查文件是否已存在
                if os.path.exists(f"{new_file_path}.nc"):
                    logger.info(f"文件已存在，跳过下载: {new_file_path}")
                    skipped_files.append(new_file_path)
                    continue

                # 转换为NetCDF格式
                if convert_to_nc:
                    nc_file_path = f"{new_file_path}.nc"
                    try:
                        grb2_to_nc(new_file_path, nc_file_path)
                        logger.info(f"成功转换为NetCDF: {nc_file_path}")

                        # 转换成功后删除原始GRIB2文件
                        if (
                            os.path.exists(nc_file_path)
                            and os.path.getsize(nc_file_path) > 0
                        ):
                            # 删除原始GRIB2文件
                            os.remove(new_file_path)
                            logger.info(f"已删除中间文件: {new_file_path}")

                            # 删除可能存在的各种格式的索引文件
                            base_dir = os.path.dirname(new_file_path)
                            base_name = os.path.basename(new_file_path)

                            # 查找并删除所有可能的索引文件
                            for file in os.listdir(base_dir):
                                if file.startswith(base_name) and (
                                    file.endswith(".idx") or ".idx." in file
                                ):
                                    idx_path = os.path.join(base_dir, file)
                                    os.remove(idx_path)
                                    #logger.info(f"已删除索引文件: {idx_path}")

                            # 将返回的文件路径改为NC文件
                            downloaded_files.append(nc_file_path)
                        else:
                            # 如果NC文件不存在或大小为0，保留原始文件
                            downloaded_files.append(new_file_path)
                    except Exception as e:
                        logger.error(f"转换为NetCDF失败: {e}")
                        # 转换失败时保留原始文件
                        downloaded_files.append(new_file_path)
                else:
                    # 不需要转换时保留原始文件
                    downloaded_files.append(new_file_path)
            else:
                logger.warning(f"下载失败: {file_name}")

        except Exception as e:
            logger.error(f"下载文件 {file_info['name']} 时出错: {e}")
            logger.error(traceback.format_exc())

    # 打印统计信息
    # if skipped_files:
    #    logger.info(f"跳过 {len(skipped_files)} 个已存在的文件")
    # if downloaded_files:
    #    logger.info(f"新下载 {len(downloaded_files)} 个文件")

    return downloaded_files


if __name__ == "__main__":
    # 设置文件路径
    ROOT_DIR = Path(__file__).parent.parent.absolute()
    bucket = "rainfall"
    minio_folder = "A_OA_data/GFS/1h/2021/03/01" # 每次只能按照天为单位
    gfs_folder = "/home/zlh/gfs_revise/data/minio/gfs_minio"
    temp_folder = "/home/zlh/gfs_revise/data/minio/temp/gfs"  # 临时文件夹
    try:
        # 创建客户端
        client = create_minio_client(
            MINIO_COLD_ENDPOINT,
            MINIO_COLD_ACCESS_KEY,
            MINIO_COLD_SECRET_KEY,
            secure=False,
        )
    except Exception as e:
        logger.error(f"程序执行出错: {e}")

    print("\n=== 批量下载指定预见期的文件并转换格式 ===")
    try:
        downloaded_files = download_folder_with_cert(
            client=client,
            bucket=bucket,
            minio_folder=minio_folder,
            local_folder=temp_folder,
            output_folder=gfs_folder,
            leads=leads,
            convert_to_nc=True,
        )
        print(f"成功下载并处理 {len(downloaded_files)} 个文件")
        for file in downloaded_files[:5]:  # 只显示前5个文件
            print(f"- {file}")
        if len(downloaded_files) > 5:
            print(f"... 以及其他 {len(downloaded_files) - 5} 个文件")
    except Exception as e:
        logger.error(f"批量下载文件夹时出错: {e}")
        logger.error(traceback.format_exc())
